<template>
  <div class="youzan">
    <div class="back">
      <router-link to="/" style="display:flex;align-items:center;">
        <svg t="1608448284439" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2561" width="200" height="200"><path d="M766.976 1021.952c-10.24 0-20.48-3.584-28.672-10.752L228.352 538.624c-15.872-14.336-15.872-38.4 0-52.736L738.304 13.312c15.872-14.336 40.96-14.336 56.832 0s15.872 38.4 0 52.736L313.856 512l481.28 445.952c15.872 14.336 15.872 38.4 0 52.736-7.68 7.168-17.92 11.264-28.16 11.264z" fill="#2c2c2c" p-id="2562"></path></svg>
        返回
      </router-link>
    </div>
    <div class="content">
      <div class="left">
        <div class="title">
          粘贴页面
        </div>
        <div style="display:flex;">
          <label class="el-form-item__label">参数预设</label>
          <el-select v-model="elenowoption" @change="loadPreoption" placeholder="加载预设">
            <el-option
              v-for="(item, index) in websiteOptions"
              :key="index"
              :label="item.label"
              :value="item.value">
            </el-option>
          </el-select>
          <label class="el-form-item__label" style="margin-left:10px;">解析文本</label>
          <el-input placeholder="粘贴内容" v-model="htmlText" clearable />
        </div>
        <div class="title" style="margin-top:10px;">
          参数调整
        </div>
        <label class="el-form-item__label">* 单击表格项编辑</label>
        <el-table
          :data="nowWebsiteOption"
          border
          style="width:100%;margin-top:10px;">
          <el-table-column
            label="类别名称"
            width="90">
            <template slot-scope="scope">
              <input v-model="scope.row.name" placeholder="类别名" type="text" class="table-fullinput">
            </template>
          </el-table-column>
          <el-table-column
            label="包裹元素">
            <template slot-scope="scope">
              <input v-model="scope.row.selecter" placeholder="选择器名" type="text" class="table-fullinput">
            </template>
          </el-table-column>
          <el-table-column
            width="230"
            label="图片正则">
            <template slot-scope="scope">
              <input v-model="scope.row.reg" placeholder="正则表达式" type="text" class="table-fullinput">
            </template>
          </el-table-column>
          <!-- el-table-column
            width="180"
            label="压缩参数(实验性,不生效)">
            <template slot-scope="scope">
              <input v-model="scope.row.compress" placeholder="宽度(px),大小(kb)" type="text" class="table-fullinput">
            </template>
          </el-table-column -->
           <el-table-column
            label="操作"
            width="50">
            <template slot-scope="scope">
              <el-button
                @click="removeSelecter(scope.$index)"
                type="text"
                size="small">
                移除
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-button @click="addselecter" size="mini" style="margin-top:5px;width:100%;">添加类别</el-button>
        <div class="title" style="margin-top:10px;">
          抓取教程
        </div>
        <div class="info">
          <svg t="1608449825620" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3589" width="200" height="200"><path d="M615.003955 846.760297H408.995021c-37.872562 0-68.669986-30.80561-68.669985-68.669986v-51.501978c0-18.960841 15.373129-34.334993 34.334993-34.334993 18.960841 0 34.334993 15.373129 34.334992 34.334993v51.501978h206.008934v-51.485605c0-18.960841 15.373129-34.334993 34.334993-34.334993s34.334993 15.373129 34.334993 34.334993v51.485605c0 37.864376-30.797423 68.669986-68.669986 68.669986zM597.836971 958.708971H426.163029c-18.960841 0-34.334993-15.373129-34.334992-34.334993s15.373129-34.334993 34.334992-34.334992h171.673942c18.960841 0 34.334993 15.373129 34.334992 34.334992s-15.374152 34.334993-34.334992 34.334993z" fill="#438DF6" p-id="3590"></path><path d="M649.356344 759.187799c-13.227256 0-25.835412-7.695263-31.485085-20.596084-7.594979-17.376763 0.352017-37.620829 17.720594-45.206598 103.775528-45.349861 168.254052-147.875932 168.254051-267.579221 0-160.928203-130.918725-291.845904-291.845904-291.845905s-291.846928 130.918725-291.846928 291.846928c0 119.694079 64.478524 222.221174 168.254052 267.579221 17.384949 7.594979 25.315572 27.829835 17.720593 45.206598-7.611352 17.376763-27.863604 25.331945-45.215807 17.712407C231.737922 699.856505 151.48411 573.213153 151.48411 425.806919c0-198.791555 161.732521-360.51589 360.51589-360.51589S872.51589 227.01434 872.51589 425.806919c0 147.415444-80.254835 274.058796-209.428823 330.498226a34.129308 34.129308 0 0 1-13.730723 2.882654z" fill="#438DF6" p-id="3591"></path><path d="M383.244544 400.407436H297.407574c0-108.86443 88.570221-197.425442 197.425441-197.425442v85.836971c-61.528332 0-111.588471 50.060139-111.588471 111.588471z" fill="#438DF6" p-id="3592"></path></svg>
          使用方法：打开要抓取的页面，按下F12打开开发者工具，切换到"元素"(element)选项卡，单击选中第二行，按下Ctrl+C复制即可
        </div>
        <img class="demoimg" src="@/assets/youzan_demo.gif">
      </div>
      <div class="right">
        <div align="center" v-if="assets == null || assets.photosPack.length == 0">
          <img src="@/assets/clipnone.svg">
          <div class="info">
            未监测到内容
          </div>
        </div>
        <div class="imglist" v-if="assets != null && assets.photosPack.length > 0">
          <div v-for="(ass, index) in assets.photosPack" :key="index">
            <div class="title" style="margin:6px 0 4px 10px;">
              {{ ass.name }}
            </div>
            <div class="mainlist">
              <div v-for="(img, index) in ass.photos" :key="index" class="jximg">
                <el-image class="img"
                  :src="img" fit="cover"
                  :preview-src-list="ass.photos">
                </el-image>
                <div class="info">{{ ass.name }}{{ index+1 }}</div>
              </div>
            </div>
          </div>
          <div @click="saveAssets" class="savebtn">打包下载</div>
        </div>
      </div>
    </div>
    <transition name="loadanim">
      <div class="loadanim" v-if="saveLoading">
        <img src="@/assets/load.svg">
        <div class="info">{{ loadinginfo }}</div>
      </div>
    </transition>
  </div>
</template>

<script>
import lrz from 'lrz'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'

export default {
  data() {
    return {
      htmlText: '',
      htmlTextBK: '',
      assets: null,
      saveLoading: false,
      loadinginfo: '',
      compressmainflag: true,
      websiteOptions: [
        {
          label: '有赞',
          value: [
            {
              name: '主图',
              selecter: '.fenxiao__detail-goods-swiper-thumblist',
              reg: '\\/\\/img\\.yzcdn\\.cn\\/upload_files\\/[^?!\\")]*'
            },
            {
              name: '详情',
              selecter: '.market-goods-desc',
              reg: '\\/\\/img\\.yzcdn\\.cn\\/upload_files\\/[^?!\\")]*'
            }
          ]
        },
        {
          label: '1688',
          value: [
            {
              name: '主图',
              selecter: '#dt-tab',
              reg: '\\/\\/cbu01\\.alicdn\\.com\\/img\\/[^&\\"\\.)]*\\.[a-zA-Z]{3,4}'
            },
            {
              name: '详情',
              selecter: '#mod-detail-description',
              reg: `\\/\\/cbu01\\.alicdn\\.com\\/img\\/[^\\")]*`
            }
          ]
        }
      ],
      nowWebsiteOption: [],
      elenowoption: ''
    }
  },
  methods: {
    explainText(fileContent) {
      // 转为 dom
      let contentDom = document.createElement("div");
      contentDom.innerHTML = fileContent;

      // title
      let titleDom = contentDom.getElementsByTagName("title")[0];
      let title = titleDom.innerText; // 经测试特殊字符在使用file-saver保存时会自动去除,无需处理

      let photosPack = [];
      let names = [];
      for(let i = 0; i < this.nowWebsiteOption.length; i++) {
        /* 完整性检测
         * name 不可使用系统字符 (应该无影响，暂时不检测)
         */
        let name = this.nowWebsiteOption[i].name;
        let selecter = this.nowWebsiteOption[i].selecter;
        let reg = this.nowWebsiteOption[i].reg;
        // 任意一项都不可为空
        if(name == "" || selecter == "") {
          continue;
        }
        // name重复性检测
        let flag = false;
        for(let j = 0; j < names.length; j++) {
          if(names[j] == name) {
            flag = true;
          }
        }
        if(flag) {
          continue;
        }
        names.push(name);
        // selecter有效性检测
        let photoDom = $(this.nowWebsiteOption[i].selecter, contentDom)[0];
        if(photoDom == undefined) {
          continue;
        }

        reg = eval('/' + reg + '/');
        let photos = getImgs(photoDom.innerHTML, reg);
        photosPack.push({
          name: this.nowWebsiteOption[i].name,
          photos: photos
        })
      }

      return {
        title: title,
        photosPack: photosPack
      }

      // 获取元素中的yzcdn图片链接
      function getImgs(domtext, reg) {
        let fileContentCopy = domtext;
        let imgList = [];

        while(true) {
          let regex = reg.exec(fileContentCopy);

          if(regex == null) {
            break;
          }

          let imgsrc = regex[0];
          fileContentCopy = fileContentCopy.substring(regex.index + imgsrc.length, fileContentCopy.length);

          imgList.push(imgsrc);
        }

        return imgList;
      }
    },
    async saveAssets() {
      this.saveLoading = true;

      let assetsObj = []
      for(let i = 0; i < this.assets.photosPack.length; i++) {
        let photos = this.assets.photosPack[i].photos;
        let name = this.assets.photosPack[i].name;
        let datas = await this.downloadByList(photos, name);
        assetsObj.push({
          name: name,
          datas: datas
        })
      }

      // 打包zip
      this.loadinginfo = `正在打包`;

      let zip = new JSZip();

      for(let i = 0; i < assetsObj.length; i++) {
        let folder = zip.folder(assetsObj[i].name);
        for(let j = 0; j < assetsObj[i].datas.length; j++) {
          let data = assetsObj[i].datas[j].base64.replace(/^data:image\/\w+;base64,/, "");
          folder.file(assetsObj[i].datas[j].name, data, {base64: true});
        }
      }
      
      zip.generateAsync({type:"blob"}).then(content => {
        // 下载
        saveAs(content, `${this.assets.title}.zip`);
        this.loadinginfo = `打包完成`;
        setTimeout(() => {
          this.saveLoading = false;
        }, 2000);
      });
    },
    // 将url数组下载转换为base64+name对象数组
    downloadByList(urlList, showname) {
      return new Promise((resolve, reject) => {
        let mainlist = [];

        let index = 0;
        let _this = this;
        (function downmain() {
          let url = urlList[index];
          let imgname = `${showname}${index+1}`;

          _this.loadinginfo = `正在准备第${index+1}个${showname}`;

          _this.compressImg(url,
            _this.compressmainflag ? 800 : null,
            _this.compressmainflag ? 500 : null).then(response => {
            mainlist.push({
              base64: response.data,
              name: `${imgname}.${response.type}`
            })
            if(++index < urlList.length) {
              downmain();
            }else {
              resolve(mainlist);
            }
          })
        })();
      })
    },
    /**
     * 根据url获取图像base64信息
     * maxwidth: 最大宽度, null表示原图宽度
     * maxkb: 最大大小(kb), null表示不压缩
     */
    compressImg(url, maxwidth, maxkb) {
      return new Promise((resolve, reject) => {
        let quality = 1;

        (function compress() {
          lrz(url, {
            width: maxwidth,
            quality: quality
          }).then(rst => {
            if(maxkb == null || rst.base64Len <= maxkb * 1024) {
              let type = /[^\.]*$/.exec(rst.origin)[0];
              resolve({
                data: rst.base64,
                quality: quality,
                type: type
              });
            }else {
              quality -= 0.1;
              compress(); // 降低质量递归压缩
            }
          });
        })();
      })
    },
    loadPreoption() {
      this.nowWebsiteOption = JSON.parse(JSON.stringify(this.elenowoption));
      this.elenowoption = '';
    },
    addselecter() {
      this.nowWebsiteOption.push({
        name: '',
        selecter: '',
        compress: ''
      })
    },
    removeSelecter(index) {
      this.nowWebsiteOption.splice(index,1);
    }
  },
  mounted() {
    this.addselecter();
  },
  watch: {
    htmlText: {
      handler: function() {
        // 过滤非html标签
        if(!/<html[^]*<\/html>/.test(this.htmlText)) {
          return;
        }
        // 抓取内容
        let jxHtml = /<html[^]*<\/html>/.exec(this.htmlText)[0];
        this.htmlTextBK = this.htmlText;
        this.htmlText = "请在右侧查看解析结果";

        let assets = this.explainText(jxHtml);
        this.assets = assets;
      }
    },
    nowWebsiteOption: {
      handler: function() {
        if(!/<html[^]*<\/html>/.test(this.htmlTextBK)) {
          return;
        }

        let jxHtml = /<html[^]*<\/html>/.exec(this.htmlTextBK)[0];
        let assets = this.explainText(jxHtml);
        this.assets = assets;
      },
      deep: true
    }
  }
}
</script>

<style lang="scss" scoped>
  $min-width: 1250px;

  .youzan {
    background: #f6f7f9;
    min-height: calc(100vh - 60px);
    padding-bottom: 30px;
    .back {
      display: flex;
      align-items: center;
      width: $min-width;
      margin: 0 auto;
      padding-top: 20px;
      svg {
        width: 20px;
        height: 20px;
        margin-right: 10px;
        path {
          fill: #202124;
        }
      }
    }
    .content {
      display: flex;
      width: $min-width;
      min-height: 50vh;
      margin: 0 auto;
      padding-top: 20px;
      .left, .right {
        width: 48%;
      }
      .left {
        margin-right: 4%;
        .title {
          font-size: 16px;
          line-height: 50px;
        }
        .info {
          display: flex;
          align-items: center;
          padding: 5px 8px;
          font-size: 12px;
          border-radius: 3px;
          background: #CDE0FF;
          line-height: 20px;
          svg {
            width: 30px;
            height: 30px;
            margin-right: 5px;
          }
        }
        .demoimg {
          width: calc(100% - 2px);
          margin-top: 30px;
          border: 1px dashed #aaa;
        }
        .text {
          width: 100%;
          height: 30px;
          text-indent: 10px;
          border-radius: 3px;
          border: 1px solid #aaa;
        }
        .btn {
          padding: 0 8px;
          border-radius: 3px;
          border: 1px solid #aaa;
          background: #eee;
          cursor: pointer;
        }
        .table-fullinput {
          border: 0;
          width: 100%;
          height: 100%;
          background: #ffffff00;
          outline: none;
        }
      }
      .right {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        img {
          width: 300px;
        }
        .info {
          color: #a8aeb8;
        }
        .imglist {
          width: 100%;
          height: 100%;
          padding: 10px;
          margin: 17px 0 20px 0;
          border: 3px dashed #ddd;
          border-radius: 10px;
          .mainlist {
            display: flex;
            flex-wrap: wrap;
            .jximg {
              width: 100px;
              margin: 10px;
              display: flex;
              flex-direction: column;
              align-items: center;
              .img {
                width: 100px;
                height: 100px;
                background-color: #eee;
              }
              .info {
                font-size: 12px;
                line-height: 26px;
              }
            }
          }
        }
        .savebtn {
          margin: 20px auto;
          cursor: pointer;
          display: flex;
          justify-content: center;
          align-items: center;
          font-size: 18px;
          color: #fff;
          width: 140px;
          height: 44px;
          border-radius: 24px;
          background: linear-gradient(136deg,#0160fe,#1bb3ff 50%,#0160fe);
          box-shadow: #3b74ff5e 0 5px 16px;
        }
      }
    }
    .loadanim {
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      width: 100vw;
      height: 100vh;
      z-index: 20;
      position: fixed;
      top: 0;
      left: 0;
      background: #000000dd;
      img {
        width: 100%;
        margin-top: -10vh;
      }
      .info {
        color: #fff;
        font-size: 20px;
      }
    }
  }

  .loadanim-enter-active {
    transition: all .5s;
  }
  .loadanim-enter {
    opacity: 0;
  }
  .loadanim-enter-to {
    opacity: 1;
  }
  .loadanim-leave-active {
    transition: all .5s ease-out;
  }
  .loadanim-leave-to {
    opacity: 0;
  }

  .el-form-item__label {
    white-space: nowrap;
  }
</style>